home *** CD-ROM | disk | FTP | other *** search
/ Chip 1996 April / CHIP 1996 aprilis (CD06).zip / CHIP_CD06.ISO / hypertxt.arj / 92 / ERT9207.CD < prev    next >
Text File  |  1995-09-15  |  15KB  |  435 lines

  1.       @VSzámok párban, pontosan, avagy júliusi rejtvényeink
  2.       megfejtéseirôl@N
  3.  
  4.       @VKezdjük barátságosan...@N
  5.  
  6.           A  feladat  egy  olyan   program  írása  volt,  mely   a
  7.       barátságos   számpárokat  állítja   elô  megadott   határig.
  8.       Emlékeztetô gyanánt: barátságos számoknak nevezzük azokat  a
  9.       számpárokat,  ahol  az  egyik  szám  nála  kisebb  osztóinak
  10.       összege egyenlô a másik számmal és viszont. Ilyen  számpárok
  11.       tízezerig:
  12.           220 és 284
  13.           1184 és 1210
  14.           2620 és 2924
  15.           5020 és 5564
  16.           6232 és 6368
  17.           Látható, hogy ezek a számpárok elég ritkán találhatók  a
  18.       számegyenesen,  tehát  a   sikerélményhez  --  jó   sok  pár
  19.       megtalálásához -- elég nagy számokat kell megvizsgálnunk. Ez
  20.       viszont  drámai  hatással  lehet  a  program   futásidejére,
  21.       legalábbis  ha  nem  járunk  el  gondosan.  A  megfejtésként
  22.       érkezett nyolc  program között  volt olyan  -- jellegzetesen
  23.       ""adj,  Uram,  de rögtön"  stílusban  készült, amúgy  mûködô
  24.       eljárás -- amely a  fentebb leírt számok megtalálására  több
  25.       mint  három  órát  fordított,  pontosabban  rabolt.  (Ebéd +
  26.       csendespihenô +  ...) Ez  bizony sok,  különösen ha  tudjuk,
  27.       hogy ezeket a számokat 10-20 másodperc alatt is megkaphatjuk
  28.       (például  Tamás  István  megoldása  szerint,  bár  az általa
  29.       választott  út  némileg  egyedi,  lásd  késôbb).  Talán  nem
  30.       tanulság nélküli  végignézni, hogy  mi módon  jutunk el  egy
  31.       elsô, alacsony hatékonysággal mûködô programkezdeménytôl  --
  32.       apró,  végeredményben  kézenfekvô  ötletekkel  --  egy közel
  33.       optimális, gyors ""végsô" változatig.
  34.           Az alapötlet algoritmusát  így írhatnánk le  (megoldóink
  35.       többsége is így indult el):
  36.           @Kbarátságos számok;
  37.           osztoösszeg(szam);
  38.         ciklus i=1-tôl szam/2-ig
  39.             ha i osztja szam-ot akkor summa=summa+i
  40.         ciklus vége
  41.           eljárás vége
  42.           ciklus szam=1-tôl hatar-ig
  43.         temp=osztoösszeg(szam)
  44.         ha osztoösszeg(temp)=szam akkor kiir(szam,temp)
  45.           ciklus vége
  46.           program vége.@N
  47.           Ennyi az egész... meg a több órás futásidô. Mit  lehetne
  48.       itt tenni, hogy kicsit ügyesebben mûködjön mûvünk?
  49.           Elsô  menetben   az  osztók   összegének  meghatározását
  50.       javítjuk, felismerve azt az elemi tényt, hogy ha a 3 osztója
  51.       a 12-nek,  akkor a  12/3 is  az. Ezzel  az ötlettel elérjük,
  52.       hogy már  nem a  számok feléig,  hanem csak  a gyökükig kell
  53.       ""elgyalogolni".   Tessék    utánaszámolni,   hány    lépést
  54.       takarítunk meg ezzel, mondjuk 100000-ig vizsgálódva! Persze,
  55.       valamit  valamiért:  jelentkezik   egy  pici  csapda,   amit
  56.       valahogyan ki kell majd kerülnünk. Tudniillik, egy osztót és
  57.       a párját (például 3 és 12/3) egyszerre kell összegeznünk, de
  58.       például a  36 esetében  ezt nem  szabad megtennünk  a 6-tal!
  59.       Azaz  lett  egy  plusz  feltételvizsgálatunk,  de  mi  ez  a
  60.       lépésszám csökkenéséhez képest!
  61.           Második apró  ötletünk: ha  az osztók  összege kisebb  a
  62.       ciklusban  éppen  soros  számnál,  már  ejtjük  is,   hiszen
  63.       korábban már megvizsgáltuk  (mellesleg így egy  számpár csak
  64.       egyszer  íródik ki).  Konkrétan: a  220-hoz megvizsgáljuk  a
  65.       284-t (s  mivel jó,  kiírjuk), de  már a  284-nél tartva, az
  66.       összegként jelentkezô 220-nak nem szedjük össze az  osztóit.
  67.       Azaz   újra   egy   további   feltétel,   de   nyerünk    az
  68.       osztószámláláson.
  69.           A harmadik  ötlet a  második továbbgondolásával  adódik:
  70.       jelöljük  meg  mindig a  ""letudott"  számokat (például  egy
  71.       logikai változóval, tömbbel) és ha véletlenül egy ilyen, már
  72.       elemzett szám kerülne elénk, akkor ugorjuk át.
  73.           A  három  kis módosítás  pillanatok  alatt beírható,  az
  74.       eredmény  pedig  meggyôzô: programunk  több  mint ötszázszor
  75.       gyorsabb  lett!  (A  tízezres  határig  menô  vizsgálat   25
  76.       másodpercet vesz igénybe egy  mezei AT-n a mellékelt  listán
  77.       látható programmal, mely Gyombolai Márton és Horváth  Sándor
  78.       munkájának ""összegyúrásával" készült.)
  79.           Némileg  másként  járt   el  Tamás  István.   Az  alábbi
  80.       számelméleti tétel  felhasználásával oldotta  meg az  osztók
  81.       összegének  elôállítását.  Tudván, hogy  ha  egy egész  szám
  82.       prímtényezôs felbontása
  83.           @Kp^a*q^b*...z^h@N
  84.           ahol @Kp,q,...z@N különbözô prímszámok, @Ka,b,...h@N a  kitevôk,
  85.       akkor az @Kn@N osztóinak összege:
  86.           sigma   =   (1+p+p^2+...+p^a)*(1+q+q^2+...+q^b)*    ...*
  87.       (1+z+z^2+...+z^h)
  88.           mely  összegbôl  ezúttal   természetesen  @Kn@N-t  le   kell
  89.       vonnunk. A függvényt felhasználva programunk még  körülbelül
  90.       kétszeresére, háromszorosára gyorsítható.
  91.           A  helyes megoldást  beküldôk közül  a sorsolás  Horváth
  92.       Sándornak  kedvezett,  a  doboznyi  Tungsram  floppyt postán
  93.       küldjük el.
  94.  
  95.  
  96.       @VPI-t csak pontosan, szépen...@N
  97.  
  98.           Mindenesetere pontosabban és szebben, mint ahogy júliusi
  99.       számunkban a rejtvény szövege megjelent. Természetesen nincs
  100.       arról szó, hogy valami titkos egyezmény született volna  eme
  101.       érdekes szám új  jelölésérôl, hogy a  jól bevált görög  betû
  102.       helyett ezentúl a ""*" lenne használatos, mint ahogy az  sem
  103.       igaz, hogy a négyzetgyök 2 az
  104.           x^2=0 egyenlet megoldása az
  105.           x^2-2=0 egyenlet helyett.
  106.           ùjfent: mea maxima  culpa... Szerencsére megoldóinkat  e
  107.       bosszantó apróságok nem zavarták meg. Kilenc helyes megoldás
  108.       érkezett.  Több  megfejtônk  --  Dudás  János,  Fodor Zsolt,
  109.       Gyombolai Márton -- tanulmány értékû dokumentációt mellékelt
  110.       programjához. A  megfejtéseket egy  kivételével mind  Pascal
  111.       nyelven írták. (Érdemes lenne egyszer komolyan  megvizsgálni
  112.       a   kérdést,  hogy   mi  lehet   az  oka   e  nyelv   ekkora
  113.       népszerûségének. Ennyire ""kitalálta" Wirth, vagy ilyen  jól
  114.       dolgoztak  a Borland  fejlesztôi --  mindenki Turbo  Pascalt
  115.       használt --, vagy valami egészen más oka van a dolognak?)
  116.           A   feladat  tehát   a  PI   elsô  100   tizedesjegyének
  117.       meghatározása volt. A megoldás két alapvetô részbôl  rakható
  118.       össze:    egyrészt    kell   egy    közelítô    eljárás   PI
  119.       meghatározására, másrészt  -- mivel  a programozási  nyelvek
  120.       implementációi   körülbelül    20   tizedesjegy    maximális
  121.       pontosságot tesznek  lehetôvé --  szükségünk van  a 100 jegy
  122.       pontosságot megvalósító  adatreprezentációra, illetve  ilyen
  123.       nagypontosságú matematikai mûveletekre. A program  megírása,
  124.       felépítése  során  több  döntési  lehetôségünk  van,  de   e
  125.       döntések  nem  függetlenek  egymástól.  Például  a  közelítô
  126.       eljárás megválasztásakor szempont  lehet a gyorsasága,  de a
  127.       programozhatósága  is:   tudniillik  mely   mûveletekre  (és
  128.       hányra) kell megírni a százjegyû aritmetikát. Lássuk elôször
  129.       a közelítés kérdését!
  130.           A megoldások tanúsága  szerint két irányban  indulhatunk
  131.       el: geometriai  vagy analízisbéli  eszközöket használva.  Az
  132.       elôbbi   módszert    egyedül   Dudás    János   választotta.
  133.       Gondolatmenete azért  érdekes, mert  bizonyítja, hogy  elemi
  134.       eszközöket használva is megoldható a feladat. Olvasónk egy 2
  135.       egység  sugarú  körbe  írt  szabályos  négyszög,  nyolcszög,
  136.       tizenhatszög,   stb.   sorozat   kerületét   számolta    ki.
  137.       Nyilvánvaló, hogy így PI  négyszereséhez -- elég gyorsan  --
  138.       közelítô  értéket  kapunk,  mindössze  a  Pithagorasz-tételt
  139.       használva. De ebbôl származik egy gond is: a sorozat képlete
  140.       négyzetgyökvonást  tartalmaz,  tehát meg  kellett  írnia egy
  141.       gyökvonó  algoritmust is,  hiszen a  beépített függvény  nem
  142.       dolgozik a kívánt pontossággal. Többi megfejtônk a  végtelen
  143.       sorok elméletéhez fordult a megfelelô közelítést  keresendô.
  144.       Volt  aki  az  úgynevezett  Leibnitz  sor  mellett  döntött,
  145.       melynek alakja:
  146.           PI/4 = 1 - 1/3 + 1/5 - 1/7 + ...
  147.           Az   egyszerû    képlettel   megadható    (az   arctg(x)
  148.       hatványsorából származó) sorral szemben egy -- ámde döntô --
  149.       érv  hozható  fel:  rendkívül  lassan  konvergál.  Hiszen  a
  150.       közelítés pontosságát jól jellemzi az utolsó számított  tag,
  151.       melynek nagysága
  152.           @K1/(2*n+1)@N
  153.           Ennek kell
  154.           @K10^-100@N <****10 a minusz századikon****>
  155.           -nál  kisebbnek  lennie,  ami  körülbelül  10^100 lépést
  156.       igényel!  A  program  futásidejének  megbecsülésére  nem  is
  157.       vállalkozom.   Gyorsabbnak  tûnik   az  arcsin(x)   függvény
  158.       hatványsorából képzett közelítés:
  159.               1  1      3     1     3∙5    1
  160.           p = 3∙[1 + ─── ─ + ─────── ── + ─────── ─── +...]
  161.              2∙3 8   22∙2!∙5 32   23∙3!∙7 128
  162.           vagy  a  Machin-féle sor  (melyet  arctg(x) segítségével
  163.       kaphatunk meg):
  164.             ┌                            ┐
  165.          pi     │ 1   1 ┌ 1 ┐3  1 ┌ 1 ┐5  1 ┌ 1 ┐7      │
  166.          -- = 4 │ - - - │ - │ + - │ - │ - - │ - │ + ... │
  167.           4     │ 5   3 └ 5 ┘   5 └ 5 ┘   7 └ 5 ┘       │
  168.             └                            ┘
  169.           ┌                              ┐
  170.           │  1    1 ┌  1  ┐3  1 ┌  1  ┐5  1 ┌  1  ┐7      │
  171.         - │ --- - - │ --- │ + - │ --- │ - - │ --- │ + ... │
  172.           │ 239   3 └ 239 ┘   5 └ 239 ┘   7 └ 239 ┘       │
  173.           └                              ┘
  174.           esetleg a Wallis-formula:
  175.           @Kpi/2 = 2/1*2/3*4/3*4/5*6/5*6/7*...@N
  176.           (Ezek,  s  más,   PI-t  eredményezô  sorok,   lánctörtek
  177.       megtalálhatók például @KCourant-Robbins: Mi a matematika@N  címû
  178.       könyvében). A megfejtések  -- melyek ezeket  a közelítéseket
  179.       alkalmazták -- azt bizonyítják,  hogy ez a három  utóbbi sor
  180.       jól  használható  (elég  gyorsak:  egy  286-oson  futtatva a
  181.       programokat 10-40 másodperc közötti idôk adódtak),  feltéve,
  182.       hogy a kiegészítô eljárások elég hatékonyak. (Volt megfejtô,
  183.       akinek  sikerült  a  Machin-sort  alkalmazva  ""15   perces"
  184.       programot írnia). Döntenünk kell a pontosság  ellenôrzésének
  185.       kérdésében:  folyamatosan,  ciklusonként  megnézzük-e  (mint
  186.       Déri  Attila),  vagy  elôzetesen  megbecsüljük  a  szükséges
  187.       lépésszámot,  s  az  alkalmazandó  jegyek  számát (Gyombolai
  188.       Márton  járt  el  így)  --  ez  gyors,  viszont  matematikai
  189.       jártasságot követel.
  190.           Nem elhanyagolható kérdés a számjegyek nyilvántartásának
  191.       ügye: választhatjuk a szöveges ábrázolást (mint tette  Fodor
  192.       Zsolt), vagy a tömbös megvalósítást (Tóth László és  mások).
  193.       A  másik  probléma  természetesen  a  mûveletek:  volt   aki
  194.       szimulálta  a  ""kézi"  eljárásokat,  volt  aki  teljesen új
  195.       algoritmusokat konstruált, mondjuk 10000-es számrendszerben.
  196.       Mindezek   eredményeképpen   a   programoknak   egy   színes
  197.       választéka  jött létre,  s kár,  hogy nem  tudjuk  mindegyik
  198.       változatot  közölni.  A   mellékelt  lista  Horváth   Sándor
  199.       programját mutatja.
  200.           Ålljon itt végezetül PI  elsô 100 tizedesjegye, mely  --
  201.       Verbôczi  Zoltán szíves  közlése nyomán  -- ellenôrizhetô  a
  202.       Középiskolai Matematikai Lapok  1983. évi 1.  számának hátsó
  203.       borítóján megtalálható 950 jegyû táblázat alapján.
  204.           14159 26535  89793 23846  26433 83279  50288 41971 69399
  205.       37510 58209 74944 59230 78164 06286 20899 86280 34825  34211
  206.       70679
  207.  
  208.       @KBánhegyesi Zoltán@N
  209.  
  210. program barat;
  211.  
  212. var  i,n,nbarat,ioszt : word;
  213. var  kesz: array[1..10000] of boolean;
  214.  
  215.  
  216. function OsztokOsszege(szam:word): word;
  217.  
  218. var i,sum,fhat: word;
  219.  
  220. begin
  221.    sum := 1;          { Az 1 biztosan osztó }
  222.    fhat := trunc(sqrt(szam))+1;
  223.    for i := 2 to fhat do
  224.      if (szam mod i) = 0 then begin
  225.           inc(sum,i);
  226.           if i<>szam div i then inc(sum,szam div i);
  227.           end;
  228.    OsztokOsszege := sum;
  229. end;
  230.  
  231.  
  232.  
  233. BEGIN
  234.    write('A felsô határ (maximum 10000):');
  235.    read(n);
  236.    for i := 1 to n do kesz[i] := false;
  237.    nbarat := 0;
  238.  
  239.    for i := 2 to n do
  240.    if not kesz[i]
  241.    then begin
  242.      if OsztokOsszege(i) > i
  243.      then begin
  244.        ioszt := OsztokOsszege(i);
  245.        if OsztokOsszege(ioszt) = i
  246.        then begin
  247.      nbarat := nbarat+1;
  248.      writeln(nbarat:4,'. barátságos számpár: ',i:5,' és ',ioszt:5);
  249.      kesz[ioszt] := true;
  250.        end;
  251.      end;
  252.      kesz[i] := true;
  253.    end;
  254. END.
  255. ---------
  256.  
  257. program Pijegyek;
  258. { Horváth Sándor, Barcs}
  259.  
  260. uses crt;
  261.  
  262. const
  263.   Jegy  = 100;
  264.   Hossz = (jegy div 4) + 3 ;
  265.  
  266. type
  267.   Longreal  = array[1..Hossz] of word;
  268.   LongReal2 = array[1..2*Hossz] of longint;
  269.  
  270. var
  271.   Long0  : LongReal;
  272.   Long00 : LongReal2;
  273.  
  274. procedure osszead(a,b : Longreal ; var c : Longreal);
  275. var
  276.   m,i : word;
  277.   z : longint;
  278. begin
  279.   c:=Long0;
  280.   m:=0;
  281.   for i:=Hossz downto 1 do begin
  282.     z:=longint(a[i])+b[i]+m;
  283.     m:=z div 10000;
  284.     c[i]:=z mod 10000;
  285.   end;
  286.   if m>0 then begin
  287.     writeln('Túlcsordulás az összeadásnál!');
  288.     halt(1);
  289.   end;
  290. end;
  291.  
  292. procedure kivon(a,b : Longreal ; var c : Longreal);
  293. var
  294.   m,i : word;
  295. begin
  296.   m:=0;
  297.   for i:=Hossz downto 1 do begin
  298.     if a[i]>=b[i]+m
  299.     then begin
  300.       c[i]:=a[i]-b[i]-m;
  301.       m:=0;
  302.     end else begin
  303.       c[i]:=10000+a[i]-b[i]-m;
  304.       m:=1;
  305.     end;
  306.   end;
  307.   if m>0 then begin
  308.     writeln('Túlcsordulás a kivonásnál!');
  309.     halt(1);
  310.   end;
  311. end;
  312.  
  313. procedure szoroz(a,b : Longreal; var c : Longreal);
  314. var
  315.   i,j,k : word;
  316.   z,mm : longint;
  317.   e : longreal2;
  318. begin
  319.   e:=Long00;
  320.   for j:=Hossz downto 1 do begin
  321.     for i:=Hossz downto 1 do begin
  322.       z:=longint(a[i])*b[j];
  323.       k:=i+j-1;
  324.       inc(e[k],z);
  325.       mm:=e[k] div 10000;
  326.       while (mm>0) and (k>0) do begin
  327.     e[k]:=e[k] mod 10000;
  328.     dec(k);
  329.     inc(e[k],mm);
  330.     mm:=e[k] div 10000;
  331.       end;
  332.       if k=0 then begin
  333.     writeln('Túlcsordulás a szorzásnál !');
  334.     halt(1);
  335.       end;
  336.     end;
  337.   end;
  338.   for i:=1 to Hossz do c[i]:=e[i];
  339. end;
  340.  
  341. procedure ByteSzoroz(a : LongReal; b : byte; var c : LongReal);
  342. var
  343.   i : word;
  344.   x,m : longint;
  345. begin
  346.   c:=Long0;
  347.   m:=0;
  348.   for i:=Hossz downto 1  do begin
  349.     x:=longint(a[i])*b+m;
  350.     c[i]:=x mod 10000;
  351.     m:=x div 10000;
  352.   end;
  353.   if m>0 then begin
  354.     writeln('Túlcsordulás a byte szorzásnál !');
  355.     halt(1);
  356.   end;
  357. end;
  358.  
  359. procedure EgyetOszt(a:longint; var c : Longreal);
  360. var
  361.   sz,m : longint;
  362.   i : word;
  363. begin
  364.   m:=0;
  365.   sz:=1;
  366.   c:=Long0;
  367.   for i:=1 to Hossz do begin
  368.     c[i]:=sz div a;
  369.     m:=sz mod a;
  370.     sz:=m*10000;
  371.   end;
  372. end;
  373.  
  374. procedure Kiir(a : LongReal);
  375. var
  376.   i : word;
  377. begin
  378.   write(a[1],'.');
  379.   for i:=2 to Hossz do
  380.   case a[i] of
  381.     0..9 : write('000',a[i]);
  382.     10..99 : write('00',a[i]);
  383.     100..999 : write('0',a[i]);
  384.     else write(a[i]);
  385.   end;
  386.   writeln;
  387. end;
  388.  
  389. function Egyenlo(a,b : LongReal) : boolean;
  390. var i : word;
  391. begin
  392.   i:=1;
  393.   while (i<=Hossz) and (a[i]=b[i]) do inc(i);
  394.   Egyenlo:=i>Hossz;
  395. end;
  396.  
  397. var
  398.   pi_Szam, Pi_regi, a1_5, a1_239, a, b, c, c1, c2 : LongReal;
  399.   sig,i : word;
  400.  
  401. begin
  402.   ClrScr;
  403.   for i:=1 to hossz do Long0[i]:=0;
  404.   for i:=1 to 2*hossz do Long00[i]:=0;
  405.   pi_Szam:=Long0;
  406.   EgyetOszt(5,a1_5);
  407.   EgyetOszt(239,a1_239);
  408.   a:=a1_5;
  409.   b:=a1_239;
  410.   szoroz(a1_5,a1_5,a1_5);
  411.   szoroz(a1_239,a1_239,a1_239);
  412.   sig:=1;
  413.   i:=1;
  414.   ClrScr;
  415.   repeat
  416.     Pi_regi:=Pi_szam;
  417.     ByteSzoroz(a,16,c1);
  418.     ByteSzoroz(b,4,c2);
  419.     Kivon(c1,c2,c);
  420.     EgyetOszt(i*2-1,c1);
  421.     Szoroz(c,c1,c);
  422.     if sig=1 then Osszead(pi_Szam,c,pi_Szam) else Kivon(pi_Szam,c,pi_Szam);
  423.     sig:=1-sig;
  424.     GotoXY(1,1);
  425.     writeln(i,'. iteráció');
  426.     szoroz(a,a1_5,a);
  427.     szoroz(b,a1_239,b);
  428.     inc(i);
  429.   until egyenlo(pi_szam,pi_regi);
  430.   writeln;
  431.   write('pi = ');
  432.   kiir(pi_szam);
  433.   repeat until keypressed;
  434. end.
  435.